package com.wetao.lib_common.utils.oss;

import android.content.Context;
import android.util.Log;

import com.alibaba.sdk.android.oss.ClientConfiguration;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSS;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback;
import com.alibaba.sdk.android.oss.common.OSSLog;
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider;
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider;
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.wetao.lib_common.Constants;
import com.wetao.lib_common.bean.response.OssInfo;
import com.wetao.lib_common.net.RetryWithDelay;
import com.wetao.lib_common.net.RxUtils;
import com.wetao.lib_common.net.ServerUtils;
import com.wetao.lib_common.net.callback.RequestCallback;
import com.wetao.lib_common.net.callback.RxErrorHandler;
import com.wetao.lib_common.utils.ComUtil;
import com.wetao.lib_common.utils.LogUtils;
import com.wetao.lib_common.utils.ThreadPool;
import com.wetao.lib_common.utils.ToastUtil;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;

import static android.os.Looper.getMainLooper;

/**
 * author: zhouyh
 * created on: 2019-11-13 16:43
 * description: 啊里 oss 文件上传
 */
public class UploadOss {


    //private static final String ENDPOINT = "http://oss-cn-beijing.aliyuncs.com";  //上传的域名
    //private static final String BUCKET = "newpage-edu";    //  上传的文件夹
    private volatile static UploadOss uploadOss;
    private OSS mOss;
    private OssInfo.DataBean ossInfoBean;
    private static Context mContext;
    private static final int ERR_COUNT_MAX = 2;
    private static int m_err_count = 0;
    private boolean isUploading = false;
    private long getKeyStartTime = 0;



    public static void init(Context context){
        mContext = context;
    }

    public static UploadOss getInstance(){
        if (uploadOss == null){
            synchronized (UploadOss.class){
                if (uploadOss == null){
                    uploadOss = new UploadOss();
                }
            }
        }
        return uploadOss;
    }

    private UploadOss() {}

    public static void clear(){
        if (uploadOss!=null){
            uploadOss = null;
        }
    }


    public boolean isUploading() {
        return isUploading;
    }

    public void setUploading(boolean uploading) {
        isUploading = uploading;
    }

    /**
     * 更新Token
     * @param
     */
    private void initOSS(final String object, final String localFile, final OnUploadListener listener, final OssParams ossParams){
        ThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                ClientConfiguration conf;
                conf = new ClientConfiguration();
                conf.setConnectionTimeout(15 * 1000); // connction time out default 15s
                conf.setSocketTimeout(15 * 1000); // socket timeout，default 15s
                conf.setMaxConcurrentRequest(5); // synchronous request number，default 5
                conf.setMaxErrorRetry(2); // retry，default 2
                OSSLog.enableLog(); //write local log file ,path is SDCard_path\OSSLog\logs.csv
                OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(
                        ossInfoBean.getAccessKeyId(), ossInfoBean.getAccessKeySecret(),ossInfoBean.getSecurityToken());
               mOss = new OSSClient(mContext, ossInfoBean.getEndPoint(), credentialProvider, conf);

                upload2OSS(object,localFile, listener, ossParams);
            }
        });

    }


    /**
     *
     * @param object 上传后服务器地址
     * @param localFile 本地文件地址
     * @param listener  监听上传事件
     */
    public void upload2OSS(final String object, final String localFile, final OnUploadListener listener, final OssParams ossParams) {
        Log.e("UploadOss","oss上传文件开始" + "localFile=" + localFile);
        if (!ComUtil.isNetworkConnected()) {
            m_err_count = 0;
            if(listener != null) {
                Log.e("UploadOss","oss上传文件失败 -- 没有网络");
                listener.uploadFail(null, localFile, ossParams);
            }
            return;
        }

        if(mOss == null || ossInfoBean == null) {
            Log.e("UploadOss","oss上传文件 -- mOSS=null");
            m_err_count++;
            getOssConf(false, false, object, localFile, listener, ossParams);
            return;
        }

        if(System.currentTimeMillis() - getKeyStartTime >= 50 * 60 * 1000) { //key超时，重新拉取
            Log.e("UploadOss","oss上传文件 -- 马上超时，重新获取配置");
            m_err_count = 0;
            getOssConf(false, false, object, localFile, listener, ossParams);
            return;
        }

        ThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                final long starttime = System.currentTimeMillis();
                setUploading(true);
                PutObjectRequest put = new PutObjectRequest(ossInfoBean.getBucketName(), object, localFile);
                OSSAsyncTask task = mOss.asyncPutObject(put, new OSSCompletedCallback<PutObjectRequest, PutObjectResult>() {
                    @Override
                    public void onSuccess(PutObjectRequest request, PutObjectResult result) {
                        Observable.just(1)
                                .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(new Observer< Integer >() {
                                    @Override
                                    public void onSubscribe(Disposable d) {
                                    }

                                    @Override
                                    public void onNext(Integer integer) {
                                        setUploading(false);
                                        Log.e("UploadOss", "oss上传文件 --  花费的时间time = " + (System.currentTimeMillis() - starttime));
                                        m_err_count = 0;
                                        //封装上传路径
                                        String serverUrl = ossInfoBean.getDomain()  + request.getObjectKey();
                                        if (listener!=null){
                                            Log.e("UploadOss","oss上传文件 -- 成功:" + serverUrl);
                                            listener.uploadSuccess(serverUrl,request.getUploadFilePath(), ossParams);
                                        }
                                    }

                                    @Override
                                    public void onError(Throwable e) {
                                    }

                                    @Override
                                    public void onComplete() {
                                    }
                                });
                    }

                    @Override
                    public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
                        Observable.just(1)
                                .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(new Observer< Integer >() {
                                    @Override
                                    public void onSubscribe(Disposable d) {
                                    }

                                    @Override
                                    public void onNext(Integer integer) {
                                        setUploading(false);
                                        Log.e("UploadOss","oss上传文件失败");
                        /*if (listener!=null){
                            listener.uploadFail(request.getObjectKey(),request.getUploadFilePath());
                        }*/

                                        // Request exception
                                        if (clientExcepion != null) {
                                            Log.e("UploadOss","oss上传文件失败 clientExcepion --》");
                                            // Local exception, such as a network exception
                                            clientExcepion.printStackTrace();

                                            //一般是传的参数错误，不需要计数，直接返回失败
                                            Log.e("UploadOss","oss上传文件失败 clientExcepion --》2");
                                            m_err_count = 0;
                                            listener.uploadFail(null, localFile, ossParams);
                                            clear();
                                            return;
                                        }
                                        if (serviceException != null) {
                                            Log.e("UploadOss","oss上传文件失败 serviceException --》");
                                            // Service exception 拒绝访问 Token失败重新获取
                                            if ("AccessDenied".equals(serviceException.getErrorCode())
                                                    || "InvalidAccessKeyId".equals(serviceException.getErrorCode())
                                                    || "InvalidBucketName".equals(serviceException.getErrorCode())
                                            ){
                                                Log.e("UploadOss","oss上传文件失败 -- 拒绝访问");
                                                //clear();
                                                //getOssConf(false,false, object, localFile, listener);
                                            }
                                            Log.e("ErrorCode", serviceException.getErrorCode());
                                            Log.e("RequestId", serviceException.getRequestId());
                                            Log.e("HostId", serviceException.getHostId());
                                            Log.e("RawMessage", serviceException.getRawMessage());
                                        }

                                        m_err_count++;
                                        if(m_err_count >= ERR_COUNT_MAX) {
                                            Log.e("UploadOss","oss上传文件失败 -- 上传次数大于最大次数");
                                            m_err_count = 0;
                                            listener.uploadFail(null, localFile, ossParams);
                                        } else {
                                            Log.e("UploadOss","oss上传文件失败 -- 再次获取OSS配置");
                                            //upload2OSS(object, localFile, listener);
                                            clear();
                                            getOssConf(false,false, object, localFile, listener, ossParams);
                                        }
                                    }

                                    @Override
                                    public void onError(Throwable e) {
                                    }

                                    @Override
                                    public void onComplete() {
                                    }
                                });
                    }
                });
            }
        });

    }


    /**
     * 重新获取Token
     * @param isDialog
     * @param cancelable
     */
    public void getOssConf(boolean isDialog, boolean cancelable, final String object, final String localFile, final OnUploadListener listener, final OssParams ossParams){
        ServerUtils.getCommonApi().getOssConf()
                .retryWhen(new RetryWithDelay(3,2))
                .compose(RxUtils.<OssInfo>getSchedulerTransformer())
                .subscribe(new RequestCallback<OssInfo>(mContext, RxErrorHandler.getInstance(),isDialog,cancelable) {
                    @Override
                    public void onNext(OssInfo baseResponse) {
                        super.onNext(baseResponse);
                        Log.e("UploadOss","oss上传文件 -- 后台请求到的oss配置：" + baseResponse.getData());
                        Thread thread1 = getMainLooper().getThread();
                        Thread thread2 = Thread.currentThread();
                        LogUtils.i("m1:-----getOssConf onNext主线程name: "+thread1.getName() );
                        LogUtils.i("m1:-----getOssConf onNext当前线程name: "+thread2.getName() );

                        if(baseResponse.getCode() == 0){
                            m_err_count = 0;
                            getKeyStartTime = System.currentTimeMillis();
                            ossInfoBean = baseResponse.getData();
                            initOSS(object,  localFile, listener, ossParams);
                        }else{
                            if(baseResponse.getCode() == Constants.HTTP_ERROR.HTTP_TOKEN_OUT) {
                                m_err_count = 0;
                                listener.uploadFail("-1", localFile, ossParams);
                            } else {
                                m_err_count++;
                                if(m_err_count >= ERR_COUNT_MAX) {
                                    Log.e("UploadOss","oss上传文件 -- 获得OSS配置失败，code = " + baseResponse.getCode() + ", 次数大于最大次数");
                                    m_err_count = 0;
                                    listener.uploadFail(null, localFile, ossParams);

                                } else {
                                    Log.e("UploadOss","oss上传文件 -- 获得OSS配置失败，code = " + baseResponse.getCode() + ", msg = " + baseResponse.getMsg() + ", 再次获取");
                                    getOssConf(false, false, object, localFile, listener, ossParams);
                                }
                                ToastUtil.initToast(baseResponse.getMsg());
                            }

                        }
                    }
                    @Override
                    public void onError(Throwable e) {
                        super.onError(e);
                        Log.e("UploadOss","oss上传文件 -- 从后台获得oss配置失败--" + e.toString());
                        m_err_count++;
                        if(m_err_count >= ERR_COUNT_MAX) {
                            Log.e("UploadOss","oss上传文件 -- 获得OSS配置失败次数大于最大次数");
                            m_err_count = 0;
                            listener.uploadFail(null, localFile, ossParams);
                        } else {
                            Log.e("UploadOss","oss上传文件 -- 获得OSS配置失败，再次获取");
                            getOssConf(false, false, object, localFile, listener, ossParams);
                        }
                    }
                });
    }

    public interface OnUploadListener{
        String uploadSuccess(String uploadFile, String localFile, OssParams ossParams);
        String uploadFail(String uploadFile, String localFile, OssParams ossParams);
    }

}

